﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Freedom.Common.Collections
{

    /// <summary>
    /// the filter condition
    /// </summary>
    public enum Condition
    {
        /// <summary>
        /// equal 
        /// </summary>
        Equal,
        /// <summary>
        /// not equal
        /// </summary>
        NotEqual,
        /// <summary>
        /// contains
        /// </summary>
        Contains,
        /// <summary>
        /// greate than
        /// </summary>
        GT,
        /// <summary>
        /// greate than or equal
        /// </summary>
        GTE,
        /// <summary>
        /// Less than
        /// </summary>
        LT,
        /// <summary>
        /// less than or equal
        /// </summary>
        LTE,
        /// <summary>
        /// In
        /// </summary>
        IN,
        /// <summary>
        /// Start with
        /// </summary>
        StartsWith,
        /// <summary>
        /// end with
        /// </summary>
        EndsWith,
        /// <summary>
        /// is null
        /// </summary>
        IsNull,
        /// <summary>
        /// like
        /// </summary>
        LIKE,
        /// <summary>
        /// not in 
        /// </summary>
        NotIn,
        /// <summary>
        /// Sort
        /// </summary>
        Order,
    }



    /// <summary>
    /// Attribute for collection filter column name
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    public class FilteColumnAttribute : Attribute
    {
        /// <summary>
        /// The filter column
        /// </summary>
        public string Column { get; private set; }

        /// <summary>
        /// column name
        /// </summary>
        /// <param name="column">column name</param>
        public FilteColumnAttribute(string column)
        {
            Column = column;
        }
    }


    /// <summary>
    /// conditon parameter
    /// </summary>
    public struct ConditionPara<T>
            where T: struct, IComparable, IConvertible, IFormattable
    {
        /// <summary>
        /// field name
        /// </summary>
        public T Field;
        /// <summary>
        /// conditon 
        /// </summary>
        public Condition Condition;
        /// <summary>
        /// the value 
        /// </summary>
        public string Value;
    }

    /// <summary>
    /// the filter helper
    /// </summary>
    public class FilterHelper<T>
            where T: struct, IComparable, IConvertible, IFormattable
    {
        /// <summary>
        /// form conditions to sql string
        /// </summary>
        /// <param name="conditions"></param>
        /// <returns></returns>
        public static string GetConditionsSQL(IEnumerable<ConditionPara<T>> conditions)
        {
            var sql = new StringBuilder();
            if (conditions == null || !conditions.Any()) return sql.ToString();

            var conditionList = conditions.ToList();
            //get filter conditions
            var filterConditions = conditionList.Where(c => c.Condition != Condition.Order).ToList();
            foreach (var filterCondition in filterConditions)
            {
                sql.Append(GetFilteColumnString(filterCondition.Field) + " " + ConvertConditionSQL(filterCondition.Condition, filterCondition.Value));
                sql.Append(" AND ");
            }
            if (filterConditions.Any()) sql.Remove(sql.Length - 4, 4);
            //get the sort conditions
            var sortConditions = conditionList.Where(c => c.Condition == Condition.Order).ToList();
            if (sortConditions.Any()) sql.Append(" ORDER BY ");

            foreach (var sortCondition in sortConditions)
            {
                sql.Append(sortCondition.Field + " " + sortCondition.Value);
                sql.Append(" , ");
            }

            if (sortConditions.Any()) sql.Remove(sql.Length - 2, 2);
            sql.Insert(0, " ");
            return sql.ToString();
        }


        /// <summary>
        /// convert the filter condition to sql
        /// </summary>
        /// <param name="condition">the condition</param>
        /// <param name="value">the param filed name</param>
        /// <returns></returns>
        private static string ConvertConditionSQL(Condition condition, string value)
        {
            if (string.IsNullOrEmpty(value)) return string.Empty;

            DateTime dateCondition;
            if (DateTime.TryParseExact(value, "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateCondition))
                value = string.Format("'{0}'", value);

            switch (condition)
            {
                case Condition.Equal:
                    return " = '" + value + "'";
                case Condition.NotEqual:
                    return " <> '" + value + "'";
                case Condition.Contains:
                    return " LIKE N'%" + value + "%'";
                case Condition.GT:
                    return " > " + value;
                case Condition.GTE:
                    return " >= " + value;
                case Condition.LT:
                    return " < " + value;
                case Condition.LTE:
                    return " <= " + value;
                case Condition.IN:
                    return " IN (" + value + ")";
                case Condition.StartsWith:
                    return " LIKE N'" + value + "%'";
                case Condition.EndsWith:
                    return " LIKE N'%" + value + "'";
                case Condition.LIKE:
                    return " LIKE N'%" + value + "%'";
                case Condition.NotIn:
                    return " NOT IN (" + value + ")";
                case Condition.IsNull:
                    return " IS NULL ";
                default:
                    return "";
            }
        }

        /// <summary>
        /// Get filte column string
        /// </summary>
        /// <param name="filteEnum">the filte enum value</param>
        /// <returns>The filte column for DB script</returns>
        public static string GetFilteColumnString(T filteEnum)
        {
            string columnName = filteEnum.ToString(CultureInfo.InvariantCulture);
            var attributes =
                (FilteColumnAttribute[]) typeof(T).GetField(columnName).GetCustomAttributes(typeof (FilteColumnAttribute), false);
            if (attributes.Length == 1)
            {
                return attributes[0].Column;
            }
            return columnName;
        }

    }
}
